package com.tianji.learning.service.impl;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.query.PageQuery;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.entity.dto.LearningPlanDTO;
import com.tianji.learning.entity.po.LearningLesson;
import com.tianji.learning.entity.po.LearningRecord;
import com.tianji.learning.entity.vo.LearningLessonVO;
import com.tianji.learning.entity.vo.LearningPlanPageVO;
import com.tianji.learning.entity.vo.LearningPlanVO;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.PlanStatus;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 学生课程表 服务实现类
 * </p>
 *
 * @author lemon
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class LearningLessonServiceImpl extends ServiceImpl<LearningLessonMapper, LearningLesson> implements ILearningLessonService {

    // 我们的feign已经写好了在tianji-api中
    // 但是注意这里是没有进行熔断的
    private final CourseClient courseClient;

    private final LearningLessonMapper learningLessonMapper;

    // 小节的feign
    private final CatalogueClient catalogueClient;

    private final LearningRecordMapper learningRecordMapper;

    /**
     * 添加用户课程
     *
     * @param userId
     * @param courseIds
     */
    @Transactional
    @Override
    public void addUserLesson(Long userId, List<Long> courseIds) {
        // TODO 还有另外一种思路就是说买过但是过期了的话我们先删除然后再添加
        // 目前使用的是第一种就是修改状态以及有效时间

        // TODO 这里可以自己判断就是说如果买过但是过期了的话就修改状态就可以了
        // 那么首先是根据我的课程id去查询
        List<LearningLesson> lessons = this.lambdaQuery().eq(LearningLesson::getUserId, userId)
                .in(LearningLesson::getCourseId, courseIds)
                .list();
        if (lessons == null || lessons.size() == 0) {


            // 1.通过feign调用course来查看这个课程的基本信息
            List<CourseSimpleInfoDTO> simpleInfoList = courseClient.getSimpleInfoList(courseIds);

            // 2.封装po，填充过期时间
            if (simpleInfoList == null || simpleInfoList.size() == 0) {
                log.error("课程信息为空");
                return;
            }
            List<LearningLesson> learningLessons = new ArrayList<>();
            for (CourseSimpleInfoDTO csiInfo : simpleInfoList) {
                LearningLesson lesson = new LearningLesson();
                lesson.setUserId(userId);
                lesson.setCourseId(csiInfo.getId());
                // 获取到过期时间
                // 获取到课程有效期 单位(月)
                Integer validDuration = csiInfo.getValidDuration();
                if (validDuration != null) {
                    // 当前时间+有效期
                    LocalDateTime now = LocalDateTime.now();
                    // 加法
                    LocalDateTime nowFinal = now.plusMonths(validDuration);
                    lesson.setCreateTime(now); // 创建时间
                    lesson.setExpireTime(nowFinal);
                }
                learningLessons.add(lesson);

            }

            // TODO 没有幂等性处理
            // 3.批量保存
            // 自己去处理幂等性

            for (LearningLesson learningLesson : learningLessons) {
                Long courseId = learningLesson.getCourseId();
                // 我们调用这个表里面的方法，去查询是否有这个课程
                LambdaQueryWrapper<LearningLesson> wrapper = new LambdaQueryWrapper<>();
                wrapper.eq(LearningLesson::getUserId, userId);
                wrapper.eq(LearningLesson::getCourseId, courseId);
                // 查询一下
                Integer count = learningLessonMapper.selectCount(wrapper);
                if (count > 0) {
                    // 说明已经存在了
                    // 不需要再次插入
                    continue;
                }
                // 不存在，就插入
                // 方法二就是先收集起来，然后在使用批量插入
                learningLessonMapper.insert(learningLesson);
            }
        } else {
            // 遍历我们看状态
            for (LearningLesson lesson : lessons) {
                // 如果是过期的话，就修改状态
                if (lesson.getStatus() == LessonStatus.EXPIRED) {

                    // 先获取course基本信息
                    CourseFullInfoDTO courseInfoById = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
                    if (courseInfoById == null) {
                        log.error("课程信息为空");
                        return;
                    }
                    // 获取到课程有效期 单位(月)
                    Integer validDuration = courseInfoById.getValidDuration();
                    if (validDuration != null) {
                        // 当前时间+有效期
                        LocalDateTime now = LocalDateTime.now();
                        // 加法
                        LocalDateTime nowFinal = now.plusMonths(validDuration);
                        lesson.setExpireTime(nowFinal);
                    }

                    // 修改状态
                    lesson.setStatus(LessonStatus.NOT_BEGIN);
                    // 更新时间
                    lesson.setUpdateTime(LocalDateTime.now());
                    // 更新
                    learningLessonMapper.updateById(lesson);
                } else {
                    // 就按照基本方法走
                    // 1.通过feign调用course来查看这个课程的基本信息
                    List<CourseSimpleInfoDTO> simpleInfoList = courseClient.getSimpleInfoList(courseIds);

                    // 2.封装po，填充过期时间
                    if (simpleInfoList == null || simpleInfoList.size() == 0) {
                        log.error("课程信息为空");
                        return;
                    }
                    List<LearningLesson> learningLessons = new ArrayList<>();
                    for (CourseSimpleInfoDTO csiInfo : simpleInfoList) {
                        LearningLesson lessonOne = new LearningLesson();
                        lessonOne.setUserId(userId);
                        lessonOne.setCourseId(csiInfo.getId());
                        // 获取到过期时间
                        // 获取到课程有效期 单位(月)
                        Integer validDuration = csiInfo.getValidDuration();
                        if (validDuration != null) {
                            // 当前时间+有效期
                            LocalDateTime now = LocalDateTime.now();
                            // 加法
                            LocalDateTime nowFinal = now.plusMonths(validDuration);
                            lessonOne.setCreateTime(now); // 创建时间
                            lessonOne.setExpireTime(nowFinal);
                        }
                        learningLessons.add(lessonOne);

                    }

                    // TODO 没有幂等性处理
                    // 3.批量保存
                    // 自己去处理幂等性

                    for (LearningLesson learningLesson : learningLessons) {
                        Long courseId = learningLesson.getCourseId();
                        // 我们调用这个表里面的方法，去查询是否有这个课程
                        LambdaQueryWrapper<LearningLesson> wrapper = new LambdaQueryWrapper<>();
                        wrapper.eq(LearningLesson::getUserId, userId);
                        wrapper.eq(LearningLesson::getCourseId, courseId);
                        // 查询一下
                        Integer count = learningLessonMapper.selectCount(wrapper);
                        if (count > 0) {
                            // 说明已经存在了
                            // 不需要再次插入
                            continue;
                        }
                        // 不存在，就插入
                        // 方法二就是先收集起来，然后在使用批量插入
                        learningLessonMapper.insert(learningLesson);

                    }
                }
            }
        }
    }


    /**
     * 分页查询我的课程
     *
     * @param query
     * @return
     */
    @Override
    public PageDTO<LearningLessonVO> queryMyLesson(PageQuery query) {

        // 1.获取当前登录人的id
        // 使用threadLocal来获取
        Long userId = UserContext.getUser();
        if (userId == null) {
            throw new BadRequestException("用户未登录");
        }

        // 2.分页查询我的课表
        Page<LearningLesson> page = this.lambdaQuery().eq(LearningLesson::getUserId, userId)
                // 这个是默认的
                .page(query.toMpPage("latest_learn_time", false));

        List<LearningLesson> records = page.getRecords();
        // 为null或者是空
        if (CollUtils.isEmpty(records)) {
            // 就是包了一层获取里面的total，size,records
            // return new PageDTO<>(page.getTotal(), page.getPages(), CollUtils.emptyList());
            // 这个是自己写的
            return PageDTO.empty(page);
        }
        List<Long> courseIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toList());
        // 3.远程调用course获取课程的基本信息
        // 获取到所有的课程信息
        List<CourseSimpleInfoDTO> simpleInfoList = courseClient.getSimpleInfoList(courseIds);
        if (CollUtils.isEmpty(simpleInfoList)) {
            throw new BizIllegalException("课程信息为空");
        }
        // 我们还要转为map因为这样我们就可以根据courseId直接获取到课程信息
        // 就不用遍历
        Map<Long, CourseSimpleInfoDTO> courseInfoMap = simpleInfoList.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, c -> c));

        // 4.封装返回结果(但是表查询出来的是po，我们需要的是vo，看缺少什么字段我们就要添加什么字段)
        List<LearningLessonVO> collect = records.stream().map(item -> {

            LearningLessonVO learningLessonVO = BeanUtils.copyBean(item, LearningLessonVO.class);

            // 获取到课程id
            Long courseId = item.getCourseId();
            // 获取到课程信息
            CourseSimpleInfoDTO courseSimpleInfoDTO = courseInfoMap.get(courseId);
            // 不为null才设置
            if (courseSimpleInfoDTO != null) {
                learningLessonVO.setCourseName(courseSimpleInfoDTO.getName());
                learningLessonVO.setCourseCoverUrl(courseSimpleInfoDTO.getCoverUrl());
                // 课程章节数量
                learningLessonVO.setSections(courseSimpleInfoDTO.getSectionNum());
            }

            return learningLessonVO;
        }).collect(Collectors.toList());

        // 使用PageDTO.of来封装
        // return new PageDTO<>(page.getTotal(), page.getPages(), list);
        return PageDTO.of(page, collect);

    }

    /**
     * 查询我的当前课程
     *
     * @return
     */
    @Override
    public LearningLessonVO queryMyCurrentLesson() {
        // 1.获取当前登录人的id
        Long userId = UserContext.getUser();

        // 2.查询我最近一次学习的课程 而且字段必须是必须学习中 status=1的  按照最近学习时间排序 降序获取第一条就可以了
        LearningLesson learningLesson = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                // 必须注意这里的状态是枚举类型
                // 你这里不用写getValue 因为
                // @EnumValue // 与数据库交互时要做类型转换的值
                //    // 作用mybatisPlus在保存db时候会把枚举的value值存入数据库
                .eq(LearningLesson::getStatus, LessonStatus.LEARNING)
                .orderByDesc(LearningLesson::getLatestLearnTime)
                // 取第一条 last("limit 1") 就可以实现
                .last("limit 1")
                // 表示只查询一条
                .one();

        /*原本写法
        LearningLesson one = this.getOne(new LambdaQueryWrapper<>(LearningLesson.class)
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getStatus, LessonStatus.LEARNING)
                .orderByDesc(LearningLesson::getLatestLearnTime)
                .last("limit 1"));*/

        if (learningLesson == null) {
            return null;
        }

        // 进行拷贝
        LearningLessonVO learningLessonVO = BeanUtils.copyBean(learningLesson, LearningLessonVO.class);


        // 3.远程调用course获取课程的基本信息
        // 获取到课程id
        Long courseId = learningLesson.getCourseId();
        // 将courseId转为list 因为我们的远程调用需要的是list
        // 后面两个表示是否需要章节和教师信息
        CourseFullInfoDTO courseInfo = courseClient.getCourseInfoById(courseId, false, false);
        if (courseInfo == null) {
            // 抛异常
            throw new BizIllegalException("课程信息为空");
        }
        learningLessonVO.setCourseName(courseInfo.getName());
        learningLessonVO.setCourseCoverUrl(courseInfo.getCoverUrl());
        // 小节数量
        learningLessonVO.setSections(courseInfo.getSectionNum());

        // 4.查询当前用户已经报名了总的课程数量
        // 当前用户能学习的课程数量并且是没有过期的
        Integer count = this.lambdaQuery().eq(LearningLesson::getUserId, userId)
                // 并且是没有过期的
                .le(LearningLesson::getExpireTime, LocalDateTime.now())
                .count();

        learningLessonVO.setCourseAmount(count);

        // 5.通过feign远程课程服务获取小节信息(已经写好了)
        // 这个也是远程调用
        // 最后一次学习的小节id
        Long latestSectionId = learningLesson.getLatestSectionId();
        // 封装为list
        // 如果你使用的Arrays.asList他会先默认开10个长度的数组
        List<CataSimpleInfoDTO> cataSimpleInfoDTOS = catalogueClient.batchQueryCatalogue(Collections.singletonList(latestSectionId));

        if (CollUtils.isEmpty(cataSimpleInfoDTOS)) {
            // 抛异常
            throw new BizIllegalException("小节信息为空");
        }
        CataSimpleInfoDTO cataSimpleInfoDTO = cataSimpleInfoDTOS.get(0);
        // 最近学习的小节名称
        learningLessonVO.setLatestSectionName(cataSimpleInfoDTO.getName());

        // 最近学习的小节序号(索引)
        learningLessonVO.setLatestSectionIndex(cataSimpleInfoDTO.getCIndex());

        // 6.封装返回结果
        return learningLessonVO;
    }

    /**
     * 删除用户课程
     *
     * @param userId
     * @param courseIds
     */
    @Transactional
    @Override
    public void deleteUserLesson(Long userId, List<Long> courseIds) {
        // 1.删除用户课程
        this.lambdaUpdate().eq(LearningLesson::getUserId, userId)
                .in(LearningLesson::getCourseId, courseIds)
                .set(LearningLesson::getStatus, LessonStatus.EXPIRED)
                // 修改
                .update();

        // 还要删除用户也就是记录
        List<LearningLesson> lessons = this.lambdaQuery().eq(LearningLesson::getUserId, userId)
                .in(LearningLesson::getCourseId, courseIds)
                .list();
        if (CollUtils.isNotEmpty(lessons)) {
            List<Long> lessonIds = lessons.stream().map(LearningLesson::getId).collect(Collectors.toList());
            // 删除用户课程记录
            LambdaQueryWrapper<LearningRecord> learningRecordLambdaQueryWrapper = new LambdaQueryWrapper<>();
            learningRecordLambdaQueryWrapper.in(LearningRecord::getLessonId, lessonIds);
            learningRecordLambdaQueryWrapper.eq(LearningRecord::getUserId, userId);
            learningRecordMapper.delete(learningRecordLambdaQueryWrapper);
        }

        return;
    }

    /**
     * 删除用户课程(修改状态)
     *
     * @param courseId
     */
    @Transactional
    @Override
    public void deleteUserLesson(Long courseId) {
        Long userId = UserContext.getUser();
        if (userId == null) {
            throw new BizIllegalException("用户未登录");
        }

        // 远程调用course服务看课程是否存在
        CourseFullInfoDTO courseInfoById = courseClient.getCourseInfoById(courseId, false, false);
        if (courseInfoById == null) {
            // 业务异常
            throw new BizIllegalException("课程不存在");
        }
        this.lambdaUpdate().eq(LearningLesson::getCourseId, courseId)
                .eq(LearningLesson::getUserId, userId)
                .set(LearningLesson::getStatus, LessonStatus.EXPIRED)
                .update();

        // 删除用户课程记录
        LambdaQueryWrapper<LearningRecord> learningRecordLambdaQueryWrapper = new LambdaQueryWrapper<>();
        learningRecordLambdaQueryWrapper.eq(LearningRecord::getLessonId, courseId);
        learningRecordLambdaQueryWrapper.eq(LearningRecord::getUserId, userId);
        learningRecordMapper.delete(learningRecordLambdaQueryWrapper);
        return;
    }

    /**
     * 校验课程是否有效 返回lessonId
     *
     * @param courseId
     * @return
     */
    @Override
    public Long isLessonValid(Long courseId) {
        // 因为没有使用异步feign远程调用的ThreadLocal会传递
        Long userId = UserContext.getUser();

        // 1.校验课程是否有效
        // 1.1远程调用course服务获取课程信息
        CourseFullInfoDTO courseInfoById = courseClient.getCourseInfoById(courseId, false, false);
        if (courseInfoById == null) {
            // 业务异常
            throw new BizIllegalException("课程不存在");
        }

        // 判断课程是否过期
        LambdaQueryWrapper<LearningLesson> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId);
        LearningLesson one = this.getOne(lambdaQueryWrapper);
        /*

        LearningLesson one = this.lambdaQuery().eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId)
                .eq(LearningLesson::getStatus, LessonStatus.EXPIRED)
                .one();
                */
        if (one == null) {
            return null;
        }
        // 2.判断课程是否过期
        LocalDateTime expireTime = one.getExpireTime();
        LocalDateTime now = LocalDateTime.now();
        // 是在之后
        if (expireTime != null && now.isAfter(expireTime)) {
            // 课程过期
            return null;
        }
        return one.getId();


    }

    /**
     * 查询课程详情
     *
     * @param courseId
     * @return
     */
    @Override
    public LearningLessonVO queryLesson(Long courseId) {
        Long userId = UserContext.getUser();
        if (userId == null) {
            throw new BizIllegalException("用户未登录");
        }
        // 1.校验课程是否有效
        CourseFullInfoDTO courseInfo = courseClient.getCourseInfoById(courseId, false, false);
        if (courseInfo == null) {
            // 业务异常
            throw new BizIllegalException("课程不存在");
        }


        LearningLesson one = this.lambdaQuery().eq(LearningLesson::getCourseId, courseId)
                .eq(LearningLesson::getUserId, userId)
                .one();
        if (one == null) {
            return null;
        }
        // 不为null就赋值
        return BeanUtils.copyBean(one, LearningLessonVO.class);


    }

    /**
     * 给课程模块的统计学习该课程的报名人数不需要知道过期没过期
     *
     * @param courseId
     * @return
     */
    @Override
    public Integer countLearningLessonByCourse(Long courseId) {
        return this.lambdaQuery().eq(LearningLesson::getCourseId, courseId)
                .count();
    }

    /**
     * 创建学习计划
     *
     * @param dto
     */
    @Override
    public void createLearningPlan(LearningPlanDTO dto) {
        // 1.获取用户id
        Long userId = UserContext.getUser();
        Long courseId = dto.getCourseId();

        // 2.校验课程是否有效
        CourseFullInfoDTO courseInfo = courseClient.getCourseInfoById(courseId, false, false);
        if (courseInfo == null) {
            // 业务异常
            throw new BizIllegalException("课程不存在");
        }
        // 查询是否加入课表并且是有效的
        LearningLesson learningLesson = this.lambdaQuery().eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId)
                .one();

        // 判断是否有效
        if (learningLesson == null) {
            // 业务异常
            throw new BizIllegalException("该课程没有加入课表");
        }

        if (learningLesson.getStatus() == LessonStatus.EXPIRED) {
            // 业务异常
            throw new BizIllegalException("课程已过期");
        }
        // 下面就表示正常的
        Integer freq = dto.getFreq();
        /*
        learningLesson.setWeekFreq(freq);
        this.updateById(learningLesson);
        */

        boolean update = this.lambdaUpdate().set(LearningLesson::getWeekFreq, freq)
                .set(LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .eq(LearningLesson::getId, learningLesson.getId())
                .update();

        if (!update) {
            throw new BizIllegalException("更新学习计划失败");
        }

        return;

    }

    /**
     * 分页查询我的课程计划
     *
     * @param query
     * @return
     */
    @Override
    public LearningPlanPageVO queryMyLearningPlan(PageQuery query) {

        // 1.获取用户id
        Long userId = UserContext.getUser();

        // 2.TODO 查询积分
        // 必须要有学习计划
        // 3.查询本周学习计划总数  learning_lesson  status in (0,1) and plan_status = 1  查询 sum
        // 聚合查询
        QueryWrapper<LearningLesson> QueryWrapper = new QueryWrapper<>();
        QueryWrapper.select("sum(week_freq) as weekFreq")
                .eq("user_id", userId)
                .in("status", LessonStatus.NOT_BEGIN, LessonStatus.LEARNING)
                // 要有学习计划
                .eq("plan_status", PlanStatus.PLAN_RUNNING);
        Map<String, Object> map = this.getMap(QueryWrapper);
        // {key 就是别名, value就是值}
        // 注意不能强转 因为是BigDecimal
        // 先转为String 再转为 Integer
        int weekFreqTotal = 0;
        if (map != null && map.get("weekFreq") != null) {
            weekFreqTotal = Integer.parseInt(map.get("weekFreq").toString());
        }

        // 4.本周实际学习的总数 learning_record 去看每一条的完成时间是否在本周内 是的话就是本周的学习计划
        // finish_time >= 本周的开始时间 and finish_time <= 本周的结束时间
        // finish为true userId
        // 注意会产生循环依赖 使用mapper
        // 实际完成的
        LocalDate now = LocalDate.now();
        Integer weekFinishPlanNums = learningRecordMapper.selectCount(Wrappers.<LearningRecord>lambdaQuery()
                .eq(LearningRecord::getUserId, userId)
                .eq(LearningRecord::getFinished, true)
                // 本周的开始时间 以及 结束时间
                .between(LearningRecord::getFinishTime, DateUtils.getWeekBeginTime(now), DateUtils.getWeekEndTime(now)));

        // TODO 查询积分

        // 5.查询课表数据 learning_lesson status in (0,1) and plan_status = 1 分页
        Page<LearningLesson> page = this.page(new Page<>(query.getPageNo(), query.getPageSize()),
                Wrappers.<LearningLesson>lambdaQuery()
                        .eq(LearningLesson::getUserId, userId)
                        .in(LearningLesson::getStatus, LessonStatus.NOT_BEGIN, LessonStatus.LEARNING)
                        .eq(LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                        .orderByDesc(LearningLesson::getId)
        );
        // 遍历
        List<LearningLesson> records = page.getRecords();
        if (CollUtils.isEmpty(records)) {
            // 里面数据返回null
            LearningPlanPageVO learningPlanPageVO = new LearningPlanPageVO();
            learningPlanPageVO.setTotal(0L);
            learningPlanPageVO.setList(new ArrayList<>());
            learningPlanPageVO.setPages(0L);
            return learningPlanPageVO;
        }
        List<Long> courseList = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toList());

        // 6.远程调用feign
        List<CourseSimpleInfoDTO> simpleInfoList = courseClient.getSimpleInfoList(courseList);
        if (CollUtils.isEmpty(simpleInfoList)) {
            throw new BizIllegalException("课程不存在");
        }
        Map<Long, CourseSimpleInfoDTO> courseInfoMap = simpleInfoList.stream()
                .collect(Collectors.toMap(CourseSimpleInfoDTO::getId, c -> c));

        // 6.1转换
        List<LearningPlanVO> learningPlanVOS = records.stream().map(item -> {
            LearningPlanVO vo = new LearningPlanVO();
            BeanUtils.copyProperties(item, vo);
            Long courseId = item.getCourseId();
            CourseSimpleInfoDTO courseSimpleInfoDTO = courseInfoMap.get(courseId);
            if (courseSimpleInfoDTO != null) {
                vo.setCourseName(courseSimpleInfoDTO.getName());
                vo.setSections(courseSimpleInfoDTO.getSectionNum());
            }

            // 本周已经学习的章节数
            // 现在最主要的就是得到lessonId
            // 这个是方法一：
            Long lessonId = item.getId();
            Integer integer = learningRecordMapper.selectCount(Wrappers.<LearningRecord>lambdaQuery()
                    .eq(LearningRecord::getUserId, userId)
                    .eq(LearningRecord::getLessonId, lessonId)
                    .eq(LearningRecord::getFinished, true)
                    .between(LearningRecord::getFinishTime, DateUtils.getWeekBeginTime(now), DateUtils.getWeekEndTime(now)));

            // 方法二：使用聚合查询 这样写的话你可以可以和上面一样转为map来实现
            /*QueryWrapper<LearningRecord> queryWrapper = new QueryWrapper<>();
            // 你可以使用select方法来指定你要查询的字段 并且可以使用as方法来指定别名也就是属性名
            // 我这里只是查询了lessonId 和 userId并且我的userId是别名 这样我就不用创建新的属性值了
            queryWrapper.select("lesson_id as lessonId", "count(*) as userId");
            queryWrapper.eq("user_id", userId)
                    .eq("finished", true)
                    .between("finish_time", DateUtils.getWeekBeginTime(now), DateUtils.getWeekEndTime(now));
            queryWrapper.groupBy("lesson_id");
            List<LearningRecord> learningRecords = learningRecordMapper.selectList(queryWrapper);
            */
            if (integer == null) {
                integer = 0;
            }

            // 本周学习的章节数
            vo.setWeekLearnedSections(integer);
            return vo;
        }).collect(Collectors.toList());


        LearningPlanPageVO vo = new LearningPlanPageVO();
        vo.setWeekTotalPlan(weekFreqTotal);
        vo.setWeekFinished(weekFinishPlanNums);
        vo.setList(learningPlanVOS);
        vo.setTotal(page.getTotal());
        vo.setPages(page.getPages());

        return vo;
    }

    /**
     * 定时任务扫描课表，将课程状态修改为已过期
     */
    @Override
    public void checkLessonStatus() {
        List<LearningLesson> lessonsList = this.list(Wrappers.<LearningLesson>lambdaQuery()
                .ne(LearningLesson::getStatus, LessonStatus.EXPIRED));

        // 遍历
        if (lessonsList == null) {
            return;
        }

        // 不为空
        lessonsList.stream().filter(item -> {
            // 过期时间
            LocalDateTime expireTime = item.getExpireTime();
            // 过期时间为空 也就是没有过期时间
            if (expireTime == null) {
                return false;
            }
            // 当前时间
            LocalDateTime now = LocalDateTime.now();
            // 过期时间小于当前时间
            return now.isAfter(expireTime);
        }).forEach(item -> {
            // 过期了
            item.setStatus(LessonStatus.EXPIRED);
            // 去更新
            this.updateById(item);
        });

    }
}